
#include "socket.h"

#ifdef _WIN32
#include <mstcpip.h>
#else
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif

using namespace as::snet;

#if defined(_WIN32)
LPFN_ACCEPTEX  BaseSocket::__accept_ex = nullptr;
LPFN_GETACCEPTEXSOCKADDRS BaseSocket::__get_accept_ex_sockaddrs = nullptr;
LPFN_CONNECTEX BaseSocket::__connect_ex = nullptr;
#endif

BaseSocket::BaseSocket()
{

}

BaseSocket::BaseSocket(socket_fd fd)
    :m_fd(fd)
{

}

BaseSocket::~BaseSocket()
{
	close();
}

bool BaseSocket::open(int af, int type, int protocol)
{
	if(m_fd == ERROR_SOCKET_FD)
	{
#ifdef _WIN32
    m_fd = WSASocketW(af, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);

	DWORD dwBytes = 0;
	if (nullptr == __accept_ex)
	{
		GUID guidAcceptEx = WSAID_ACCEPTEX;
		(void)WSAIoctl(this->m_fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidAcceptEx, sizeof(guidAcceptEx), &__accept_ex, sizeof(__accept_ex), &dwBytes, nullptr,
					   nullptr);
	}

	if (nullptr == __connect_ex)
	{
		GUID guidConnectEx = WSAID_CONNECTEX;
		(void)WSAIoctl(this->m_fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidConnectEx, sizeof(guidConnectEx), &__connect_ex, sizeof(__connect_ex), &dwBytes,
					   nullptr, nullptr);
	}

	if (nullptr == __get_accept_ex_sockaddrs)
	{
		GUID guidGetAcceptExSockaddrs = WSAID_GETACCEPTEXSOCKADDRS;
		(void)WSAIoctl(this->m_fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidGetAcceptExSockaddrs, sizeof(guidGetAcceptExSockaddrs), &__get_accept_ex_sockaddrs,
					   sizeof(__get_accept_ex_sockaddrs), &dwBytes, nullptr, nullptr);
	}
#else
    m_fd = ::socket(af, type, protocol);
	if(nullptr == __accept_ex)
	{
		__accept_ex = &::accept
	}
#endif
	}

	return isopen();
}

bool BaseSocket::reopen(int af, int type, int protocol)
{
	close();
	return open(af, type, protocol);
}

socket_fd BaseSocket::create_socket(int af, int type, int protocol)
{
    socket_fd fd;
#ifdef _WIN32
    fd = WSASocketW(af, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
#else
    fd = ::socket(af, type, protocol);
#endif
    return fd;
}

BaseSocket* BaseSocket::create_socket_obj(int af, int type, int protocol)
{
	auto s = new BaseSocket();
	if(s->open(af, type, protocol) == false)
	{
		delete s;
		return nullptr;
	}
	
	return s;
}

int BaseSocket::bind(const char *ip, unsigned short port) const
{
	return bind(EndPoint(ip, port));
}

int BaseSocket::bind(const EndPoint &ep) const
{
	return ::bind(this->m_fd, &ep.sa_, ep.len_);
}

int BaseSocket::connect(const char *ip, unsigned short port) const
{
	return connect(EndPoint(ip, port));
}

int BaseSocket::connect(const EndPoint &ep) const
{
	return ::connect(m_fd, &ep.sa_, ep.len_);
}

int BaseSocket::listen(int backlog)
{
	return ::listen(m_fd, backlog);
}

accept_result BaseSocket::accept()
{
	socket_fd fd = ::accept(m_fd, nullptr, nullptr);
	if(isopen(fd) == false)
	{
		return {nullptr, "accepf fd error"};
	}

	auto accSocket = new BaseSocket(fd);
	accSocket->set_non_block();
	return {accSocket, "SUCCESS"};
}

#ifdef _WIN32
bool BaseSocket::acceptex(socket_fd listen_fd, socket_fd ac_fd, PVOID lpOutputBuffer, DWORD dwReceiveDataLength, DWORD dwLocalAddressLength, DWORD dwRemoteAddressLength, LPDWORD lpdwBytesReceived, LPOVERLAPPED lpOverlapped)
{
	return __accept_ex(listen_fd, ac_fd, lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength, lpdwBytesReceived, lpOverlapped);
}

bool BaseSocket::connect_ex(socket_fd s, const struct sockaddr* name, int namelen, PVOID lpSendBuffer, DWORD dwSendDataLength, LPDWORD lpdwBytesSent, LPOVERLAPPED lpOverlapped)
{
  return __connect_ex(s, name, namelen, lpSendBuffer, dwSendDataLength, lpdwBytesSent, lpOverlapped);
}

#endif

int BaseSocket::get_last_err()
{
#ifdef _WIN32
	return ::WSAGetLastError();
#else
	return errno;
#endif
}

void BaseSocket::set_last_err(int error)
{
#ifdef _WIN32
	::WSASetLastError(error);
#else
	errno = error;
#endif
}

void BaseSocket::close(socket_fd fd)
{
	if(isopen(fd))
	{
#ifdef _WIN32
    ::closesocket(fd);
#else
    ::close(fd);
#endif
	}
}

void BaseSocket::close()
{
    close(m_fd);
	m_fd = ERROR_SOCKET_FD; 
}

void BaseSocket::set_non_block()
{
	set_non_block(m_fd);
}

void BaseSocket::set_non_block(socket_fd fd)
{
#ifdef _WIN32    
	unsigned long set = 1;
	ioctlsocket(fd, FIONBIO, &set);
#else
	int flags = fcntl(this->, , F_GETFL, 0);
	fcntl(this->, , F_SETFL, flags | O_NONBLOCK);
#endif
}

void BaseSocket::set_keep_alive(socket_fd fd)
{
#ifdef _WIN32
	tcp_keepalive alive_in;
	tcp_keepalive alive_out;
	alive_in.onoff = TRUE;
	alive_in.keepalivetime = 1000 * 60;
	alive_in.keepaliveinterval = 1000 * 10;
	unsigned long ulBytesReturn = 0;
	if (SOCKET_ERROR == WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive_in, sizeof(alive_in), &alive_out, sizeof(alive_out), &ulBytesReturn, NULL, NULL))
	{ 
	}
#else
    int opt = 1;
    setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
#endif
}

void BaseSocket::set_keep_alive()
{
    BaseSocket::set_keep_alive(m_fd);
}

int BaseSocket::recv(socket_fd fd, void *buf, int len, int flags /*= 0*/)
{
	return ::recv(fd, (char*)buf, len, flags);
}

int BaseSocket::send(socket_fd fd, void *buf, int len, int flags /* = 0*/)
{
	return ::send(fd, (char*)buf, len, flags);
}
